package com.horizon.common.service;

import com.horizon.common.annotation.NoSync;
import com.horizon.common.entity.IBaseEntity;
import com.horizon.common.repository.HQLInfo;
import com.horizon.common.repository.IBaseRepository;
import com.horizon.common.repository.Page;
import com.horizon.common.util.IDGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 基本业务逻辑操作基类
 *
 * @author wangfuwei
 * @version 2018-08-07
 */
@Service
public class BaseService<S extends IBaseEntity> implements IBaseService<S> {

    @Autowired
    protected IBaseRepository baseRepository;

    /**
     * 子类可重写此方法注入个性化业务Repository
     *
     * @return
     */
    public IBaseRepository getRepository() {
        return baseRepository;
    }

    /**
     * 持久化实体类
     *
     * @param entity
     */
    @Override
    public <T extends IBaseEntity> void persist(T entity) {
        baseRepository.persist(entity);
    }

    /**
     * 删除实体类
     *
     * @param entity
     */
    @Override
    public <T extends IBaseEntity> void remove(T entity) {
        getRepository().remove(entity);
    }

    /**
     * 删除实体类
     *
     * @param fdId
     */
    @Override
    public void remove(String fdId) {
        remove(find(fdId));
    }

    /**
     * 删除实体类
     *
     * @param fdIdList
     */
    @Override
    public void remove(List<String> fdIdList) {
        for (String fdId : fdIdList) remove(fdId);
    }

    /**
     * 保存或更新实体类
     *
     * @param entity
     * @return
     */
    @Override
    public <T extends IBaseEntity> T merge(T entity) {
        return getRepository().merge(entity);
    }

    /**
     * 根据主键查询实体类
     *
     * @param entityClass 实体类 Class
     * @param fdId        主键
     * @return
     * @throws Exception
     */
    @Override
    public <T extends IBaseEntity> T find(Class<T> entityClass, String fdId) {
        return getRepository().find(entityClass, fdId);
    }

    /**
     * 根据主键查询实体类
     *
     * @param entityClass 实体类 Class
     * @param fdIdList    主键
     * @return
     * @throws Exception
     */
    @Override
    public <T extends IBaseEntity> List<T> find(Class<T> entityClass, List<String> fdIdList) {
        List<T> rtnList = new ArrayList<>();
        for (String fdId : fdIdList) {
            rtnList.add(find(entityClass, fdId));
        }
        return rtnList;
    }

    /**
     * 根据主键查询实体类
     *
     * @param fdId 主键
     * @return
     * @throws Exception
     */
    @Override
    public S find(String fdId) {
        return find(getActualClass(), fdId);
    }

    /**
     * 根据主键查询实体类
     *
     * @param fdIdList 主键
     * @return
     * @throws Exception
     */
    @Override
    public List<S> find(List<String> fdIdList) {
        List<S> rtnList = new ArrayList<>();
        for (String fdId : fdIdList) {
            rtnList.add(find(fdId));
        }
        return rtnList;
    }

    /**
     * 根据主键查询实体类(懒加载)
     *
     * @param entityClass 实体类 Class
     * @param fdId        主键
     * @return
     * @throws Exception
     */
    @Override
    public <T extends IBaseEntity> T getReference(Class<T> entityClass, String fdId) {
        return getRepository().getReference(entityClass, fdId);
    }

    /**
     * 根据主键查询实体类(懒加载)
     *
     * @param entityClass 实体类 Class
     * @param fdIdList    主键
     * @return
     * @throws Exception
     */
    @Override
    public <T extends IBaseEntity> List<T> getReference(Class<T> entityClass, List<String> fdIdList) {
        List<T> rtnList = new ArrayList<>();
        for (String fdId : fdIdList) {
            rtnList.add(getReference(entityClass, fdId));
        }
        return rtnList;
    }

    /**
     * 根据主键查询实体类(懒加载)
     *
     * @param fdId 主键
     * @return
     * @throws Exception
     */
    @Override
    public S getReference(String fdId) {
        return getReference(getActualClass(), fdId);
    }

    /**
     * 根据主键查询实体类(懒加载)
     *
     * @param fdIdList 主键
     * @return
     * @throws Exception
     */
    @Override
    public List<S> getReference(List<String> fdIdList) {
        List<S> rtnList = new ArrayList<>();
        for (String fdId : fdIdList) {
            rtnList.add(getReference(fdId));
        }
        return rtnList;
    }

    /**
     * 根据主键判断存在持久化实体类
     *
     * @param entityClass 实体类 Class
     * @param fdId        主键
     * @param <T>
     * @return
     */
    @Override
    public <T extends IBaseEntity> Boolean isExist(Class<T> entityClass, String fdId) {
        return getRepository().isExist(entityClass, fdId);
    }

    /**
     * 根据主键判断存在持久化实体类
     *
     * @param fdId 主键
     * @return
     */
    @Override
    public Boolean isExist(String fdId) {
        return isExist(getActualClass(), fdId);
    }

    /**
     * 根据HQL语句查询列表数据
     *
     * @param hqlInfo
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> findList(HQLInfo hqlInfo) {
        if (hqlInfo.getModelName() == null || hqlInfo.getModelName().isEmpty()) {
            hqlInfo.setModelName(getActualClass().getName());
        }
        return getRepository().findList(hqlInfo);
    }

    /**
     * 根据查询条件查询列表数据
     *
     * @param entityClass 实体类 Class
     * @param whereBlock  查询条件
     * @param orderBy     排序字段
     * @param <T>
     * @return
     */
    @Override
    public <T extends IBaseEntity> List<T> findList(Class<T> entityClass, String whereBlock, String orderBy) {
        return findList(entityClass, null, whereBlock, orderBy);
    }

    /**
     * 根据查询条件查询列表数据
     *
     * @param whereBlock 查询条件
     * @param orderBy    排序字段
     * @return
     */
    @Override
    public List<S> findList(String whereBlock, String orderBy) {
        return findList("", whereBlock, orderBy);
    }

    /**
     * 根据查询条件查询列表数据
     *
     * @param entityClass 实体类 Class
     * @param selectBlock 查询条件
     * @param whereBlock  查询条件
     * @param orderBy     排序字段
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> findList(Class<T> entityClass, String selectBlock, String whereBlock, String orderBy) {
        HQLInfo hqlInfo = new HQLInfo(entityClass.getName());
        hqlInfo.setSelectBlock(selectBlock);
        hqlInfo.setWhereBlock(whereBlock);
        hqlInfo.setOrderBy(orderBy);
        return findList(hqlInfo);
    }

    /**
     * 根据查询条件查询列表数据
     *
     * @param selectBlock 查询条件
     * @param whereBlock  查询条件
     * @param orderBy     排序字段
     * @return
     */
    @Override
    public <T> List<T> findList(String selectBlock, String whereBlock, String orderBy) {
        HQLInfo hqlInfo = new HQLInfo(getActualClass().getName());
        hqlInfo.setSelectBlock(selectBlock);
        hqlInfo.setWhereBlock(whereBlock);
        hqlInfo.setOrderBy(orderBy);
        return findList(hqlInfo);
    }

    /**
     * 根据属性查找列表数据
     *
     * @param entityClass  实体类 Class
     * @param propertyName 属性名称
     * @param value        属性值
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> findByProperty(Class<T> entityClass, String propertyName, Object value) {
        HQLInfo hqlInfo = new HQLInfo(entityClass.getName());
        hqlInfo.setWhereBlock(propertyName + " = :value");
        hqlInfo.setParameter("value", value);
        return findList(hqlInfo);
    }

    /**
     * 根据属性查找列表数据
     *
     * @param propertyName 属性名称
     * @param value        属性值
     * @return
     */
    @Override
    public List<S> findByProperty(String propertyName, Object value) {
        return findByProperty(getActualClass(), propertyName, value);
    }

    /**
     * 根据HQL语句查询分页数据
     *
     * @param hqlInfo
     * @return
     */
    @Override
    public Page findPage(HQLInfo hqlInfo) {
        if (hqlInfo.getModelName() == null || hqlInfo.getModelName().isEmpty()) {
            hqlInfo.setModelName(getActualClass().getName());
        }
        return getRepository().findPage(hqlInfo);
    }

    /**
     * 与数据库同步实体类
     *
     * @param entity
     * @param <T>
     * @return
     */
    public <T extends IBaseEntity> T syncEntity(IBaseEntity... entity) {
        if (entity.length == 0 || entity[0] == null) return null;
        if (entity[0].getFdId() == null || entity[0].getFdId().isEmpty()) entity[0].setFdId(IDGenerator.generateID());

        //从数据库查询当前实体状态
        IBaseEntity dbBaseEntity = find(entity[0].getClass(), entity[0].getFdId());

        Set<Field> fileSet = new HashSet<>();
        Class tempClass = entity[0].getClass();
        while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {
            fileSet.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass();
        }
        Iterator<Field> iterator = fileSet.iterator();
        while (iterator.hasNext()) {
            Field field = iterator.next();
            field.setAccessible(true);
            if (field.getAnnotation(NoSync.class) != null) continue;

            Object value = ReflectionUtils.getField(field, entity[0]);
            //当前实体类的属性值为空，则从数据库实体类复制
            if (value == null && dbBaseEntity != null) {
                ReflectionUtils.setField(field, entity[0], ReflectionUtils.getField(field, dbBaseEntity));
            }

            //@OneToMany 一对多关系，一的一端集合级联同步多的一端的实体类
            OneToMany oneToMany = field.getAnnotation(OneToMany.class);
            if (oneToMany != null && value != null) {
                Collection<IBaseEntity> collection = (Collection<IBaseEntity>) value;
                Collection<IBaseEntity> newCollection = new ArrayList<>();

                //循环递归同步多的一端的数据库状态，并临时保存
                for (IBaseEntity baseEntity : collection) {
                    //一的一端端放弃维护关联关系
                    if (oneToMany.mappedBy() != null && !oneToMany.mappedBy().isEmpty()) {
                        newCollection.add(syncEntity(baseEntity, entity[0]));
                    } else {
                        newCollection.add(syncEntity(baseEntity));
                    }
                }

                collection.clear();
                collection.addAll(newCollection);
                ReflectionUtils.setField(field, entity[0], collection);
            }

            //@ManyToOne 多对一关系，一的一端放弃维护关联关系，由多的一端维护关联关系，自动设置所属关系
            if (field.getAnnotation(ManyToOne.class) != null && entity.length == 2) {
                ReflectionUtils.setField(field, entity[0], entity[1]);
            }
        }

        return (T) entity[0];
    }

    /**
     * 获取泛型类的实际Class
     *
     * @return
     */
    private Class<S> getActualClass() {
        Type superType = getClass().getGenericSuperclass();
        if (superType != null && superType instanceof ParameterizedType) {
            Type[] types = ((ParameterizedType) superType).getActualTypeArguments();
            if (types.length > 0) {
                try {
                    return (Class<S>) Class.forName(types[0].getTypeName());
                } catch (Exception e) {
                }
            }
        }
        return null;
    }
}
